package com.tengju.data.infrastructure.repository;

import com.google.common.collect.Lists;
import com.tengju.data.domain.model.analysis.Row;
import com.tengju.data.domain.model.report.AchievementBonus;
import com.tengju.data.domain.model.report.AchievementBonusLog;
import com.tengju.data.domain.model.report.AchievementBonusLogRepository;
import com.tengju.data.domain.model.user.UserIdCode;
import com.tengju.data.infrastructure.persistence.mybatis.dataobject.AchievementBonusDO;
import com.tengju.data.infrastructure.persistence.mybatis.dataobject.AchievementBonusLogDO;
import com.tengju.data.infrastructure.persistence.mybatis.mapper.tengju.custom.AchievementBonusLogDOCustomMapper;
import com.tengju.data.infrastructure.repository.convert.AchievementBonusConvert;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author chenhang
 * @Date 2020/12/28
 */
@Repository
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class AchievementBonusLogRepositoryImpl implements AchievementBonusLogRepository {

    private final AchievementBonusLogDOCustomMapper achievementBonusLogDOCustomMapper;

    private static final Integer BATCH_QUERY_LIMIT = 10000;

    @Override
    public List<AchievementBonus> listByLogTimeAndMonth(LocalDateTime logTime, Integer month) {
        List<AchievementBonus> result = new ArrayList<>();
        if (logTime == null || month == null) {
            return result;
        }
        int count = countByLogTimeAndMonth(logTime,month);
        int offset = 0;
        while (offset <= count) {
            List<AchievementBonusDO> bonusDOList = achievementBonusLogDOCustomMapper.listWithPage(logTime, month, offset, BATCH_QUERY_LIMIT);
            offset += BATCH_QUERY_LIMIT;
            if (CollectionUtils.isEmpty(bonusDOList)) {
                break;
            }
            List<AchievementBonus> collect = bonusDOList.stream()
                    .map(AchievementBonusConvert::convertToModel)
                    .collect(Collectors.toList());
            result.addAll(collect);
        }
        return result;
    }

    @Override
    public int countByLogTimeAndMonth(LocalDateTime logTime, Integer month) {
        return achievementBonusLogDOCustomMapper.count(logTime, month);
    }

    @Override
    public List<AchievementBonus> listByLeaderUser(LocalDateTime logTime, Integer month, Long idCode) {
        List<AchievementBonus> result = new ArrayList<>();
        if (logTime == null || month == null || idCode == null) {
            return result;
        }
        List<Long> pIdCodeList = List.of(idCode);
        List<AchievementBonus> leaderBonusList = listByUsers(logTime, month, pIdCodeList);
        if (CollectionUtils.isEmpty(leaderBonusList)) {
            return result;
        }
        result.addAll(leaderBonusList);
        while (!CollectionUtils.isEmpty(pIdCodeList)) {
            List<AchievementBonus> achievementBonusList = listByParents(logTime, month, pIdCodeList);
            if (CollectionUtils.isEmpty(achievementBonusList)) {
                break;
            }
            pIdCodeList = achievementBonusList.stream()
                    .map(v -> v.getIdCode().getValue())
                    .collect(Collectors.toList());
            result.addAll(achievementBonusList);
        }
        return result;
    }

    @Override
    public List<AchievementBonus> listSettleByLeaderUser(LocalDateTime logTime,
                                                         Integer month,
                                                         Integer teamRatioType,
                                                         Long idCode) {
        List<AchievementBonus> result = new ArrayList<>();
        if (logTime == null || month == null || idCode == null || teamRatioType == null) {
            return result;
        }
        List<Long> pIdCodeList = List.of(idCode);
        List<AchievementBonus> leaderBonusList = listByUsers(logTime, month, pIdCodeList);
        if (CollectionUtils.isEmpty(leaderBonusList)) {
            return result;
        }
        leaderBonusList = leaderBonusList.stream()
                .filter(v -> v.getTeamRatioType().equals(teamRatioType))
                .collect(Collectors.toList());
        result.addAll(leaderBonusList);
        while (!CollectionUtils.isEmpty(pIdCodeList)) {
            List<AchievementBonus> achievementBonusList = listBySettleParents(logTime, month,teamRatioType, pIdCodeList);
            if (CollectionUtils.isEmpty(achievementBonusList)) {
                break;
            }
            pIdCodeList = achievementBonusList.stream()
                    .map(v -> v.getIdCode().getValue())
                    .collect(Collectors.toList());
            result.addAll(achievementBonusList);
        }
        return result;
    }

    @Override
    public LocalDateTime queryMaxLogTime(UserIdCode idCode, Integer month) {
        return achievementBonusLogDOCustomMapper.queryMaxLogTime(idCode.getValue(),month);
    }

    @Override
    public List<AchievementBonus> listByParents(@NotNull LocalDateTime logTime,
                                                @NotNull Integer month,
                                                List<Long> allPIdCodeList) {
        if (CollectionUtils.isEmpty(allPIdCodeList)) {
            return Collections.emptyList();
        }
        List<AchievementBonus> result = new ArrayList<>();
        List<List<Long>> pIdCodeListPartition = Lists.partition(allPIdCodeList, 1000);
        for (List<Long> pIdCodeList : pIdCodeListPartition) {
            List<AchievementBonusDO> bonusDOList = achievementBonusLogDOCustomMapper.listByParents(logTime, month, pIdCodeList);
            List<AchievementBonus> collect = bonusDOList.stream()
                    .map(AchievementBonusConvert::convertToModel)
                    .collect(Collectors.toList());
            result.addAll(collect);
        }
        return result;
    }

    public List<AchievementBonus> listBySettleParents(@NotNull LocalDateTime logTime,
                                                      @NotNull Integer month,
                                                      @NotNull Integer teamRatioType,
                                                      List<Long> allSettlePIdCodeList) {
        if (CollectionUtils.isEmpty(allSettlePIdCodeList)) {
            return Collections.emptyList();
        }
        List<AchievementBonus> result = new ArrayList<>();
        List<List<Long>> settlePIdCodeListPartition = Lists.partition(allSettlePIdCodeList, 1000);
        for (List<Long> settlePIdCodeList : settlePIdCodeListPartition) {
            List<AchievementBonusDO> bonusDOList = achievementBonusLogDOCustomMapper.listBySettleParents(logTime, month, teamRatioType, settlePIdCodeList);
            List<AchievementBonus> collect = bonusDOList.stream()
                    .map(AchievementBonusConvert::convertToModel)
                    .collect(Collectors.toList());
            result.addAll(collect);
        }
        return result;
    }

    public List<AchievementBonus> listByUsers(@NotNull LocalDateTime logTime,
                                              @NotNull Integer month,
                                              List<Long> allIdCodeList) {
        if (CollectionUtils.isEmpty(allIdCodeList)) {
            return Collections.emptyList();
        }
        List<AchievementBonus> result = new ArrayList<>();
        List<List<Long>> idCodeListPartition = Lists.partition(allIdCodeList, 1000);
        for (List<Long> idCodeList : idCodeListPartition) {
            List<AchievementBonusDO> bonusDOList = achievementBonusLogDOCustomMapper.listByUsers(logTime, month, idCodeList);
            List<AchievementBonus> collect = bonusDOList.stream()
                    .map(AchievementBonusConvert::convertToModel)
                    .collect(Collectors.toList());
            result.addAll(collect);
        }
        return result;
    }

    @Override
    public List<Row> listLogTimeWithMonth() {
        List<HashMap<String, Object>> logTimeWithMonths = achievementBonusLogDOCustomMapper.listLogTimeWithMonth();
        if (CollectionUtils.isEmpty(logTimeWithMonths)) {
            return Collections.emptyList();
        }
        return logTimeWithMonths.stream()
                .map(Row::new)
                .collect(Collectors.toList());
    }

    @Override
    public void archive(LocalDateTime logTime, String archiveTableName) {
        achievementBonusLogDOCustomMapper.archive(logTime,archiveTableName);
    }

    @Override
    public void deleteWithLogTime(LocalDateTime logTime) {
        achievementBonusLogDOCustomMapper.deleteWithLogTime(logTime);
    }

    @Override
    public List<AchievementBonusLog> listWithRange(long idCode, int month, LocalDate startDate, LocalDate endDate) {
        List<AchievementBonusLogDO> bonusDOList = achievementBonusLogDOCustomMapper.listWithRange(idCode,month,startDate,endDate);
        if (CollectionUtils.isEmpty(bonusDOList)) {
            return Collections.emptyList();
        }
        return bonusDOList.stream()
                .map(AchievementBonusConvert::convertToLogModel)
                .collect(Collectors.toList());
    }

    @Override
    public List<LocalDateTime> queryLogTime(long idCode, long month, LocalDateTime startTime, LocalDateTime endTime) {
        return achievementBonusLogDOCustomMapper.queryLogTime(idCode,month,startTime,endTime);
    }


}
